package org.summerframework.web.config;

import com.fasterxml.jackson.datatype.jsr310.deser.*;
import com.fasterxml.jackson.datatype.jsr310.ser.*;
import org.summerframework.core.interceptor.AbstractHandlerInterceptorAdapter;
import org.summerframework.core.resolver.RenamingProcessor;
import org.summerframework.core.util.SpringContextUtil;
import org.summerframework.web.exception.resolver.UriExposeExceptionResolver;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.autoconfigure.jackson.JacksonProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.List;

/**
 * @author 石超
 * @version v1.0.0
 */
@EnableWebMvc
@Configuration
@ComponentScan("org.summerframework.web")
public class WebConfiguration implements WebMvcConfigurer {
    @Resource
    private SpringContextUtil springContextUtil;

    @Resource
    private MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter;

    @Autowired(required = false)
    private List<AbstractHandlerInterceptorAdapter> abstractHandlerInterceptorAdapters;

    @Resource
    private UriExposeExceptionResolver uriExposeExceptionResolver;


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        if (CollectionUtils.isEmpty(abstractHandlerInterceptorAdapters)) {
            return;
        }
        AnnotationAwareOrderComparator.sort(abstractHandlerInterceptorAdapters);
        abstractHandlerInterceptorAdapters.forEach(registry::addInterceptor);
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new StringHttpMessageConverter());
        converters.add(mappingJackson2HttpMessageConverter);
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(renamingProcessor());
    }

    @Bean
    public RenamingProcessor renamingProcessor() {
        return new RenamingProcessor(true);
    }

    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        resolvers.add(uriExposeExceptionResolver);
    }

    @Configuration
    @ConditionalOnMissingBean(name = "defaultJackson2ObjectMapperBuilderCustomizer")
    @EnableConfigurationProperties(JacksonProperties.class)
    public static class JacksonConfiguration {
        @Bean
        public Jackson2ObjectMapperBuilderCustomizer defaultJackson2ObjectMapperBuilderCustomizer(JacksonProperties jacksonProperties) {
            return builder -> {
                DateTimeFormatter dateFormat = StringUtils.isNoneBlank(jacksonProperties.getDateFormat()) ? DateTimeFormatter.ofPattern(jacksonProperties.getDateFormat()) : DateTimeFormatter.ISO_LOCAL_DATE_TIME;
                builder
                        .serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(dateFormat))
                        .serializerByType(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE))
                        .serializerByType(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ISO_LOCAL_TIME))
                        .serializerByType(Year.class, new YearSerializer(DateTimeFormatter.ofPattern("yyyy")))
                        .serializerByType(YearMonth.class, new YearMonthSerializer(DateTimeFormatter.ofPattern("yyyy-MM")))
                        .serializerByType(MonthDay.class, new MonthDaySerializer(DateTimeFormatter.ofPattern("MM-dd")))
                        .deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(dateFormat))
                        .deserializerByType(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE))
                        .deserializerByType(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ISO_LOCAL_TIME))
                        .deserializerByType(Year.class, new YearDeserializer(DateTimeFormatter.ofPattern("yyyy")))
                        .deserializerByType(YearMonth.class, new YearMonthDeserializer(DateTimeFormatter.ofPattern("yyyy-MM")))
                        .deserializerByType(MonthDay.class, new MonthDayDeserializer(DateTimeFormatter.ofPattern("MM-dd")));
            };
        }
    }
}
